#include "Common/Future.hh"

#include <iostream>

#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <boost/bind/placeholders.hpp>

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE FutureTests
#include <boost/test/unit_test.hpp>

struct Integer
{
	typedef boost::shared_ptr< Integer > Ptr;

	int value;

	Integer( int _value ) : value(_value) {};
};

class IntegerFuture : public Common::Future< Integer, Integer >
{
	public:
		typedef boost::shared_ptr< IntegerFuture > Ptr;

	public:
		IntegerFuture() : callbackCount(0) {}

		int callbackCount;
};

void IntegerFutureCallback( const Common::AbstractFuture::Ptr &aFuture )
{
	IntegerFuture::Ptr f = boost::dynamic_pointer_cast< IntegerFuture >( aFuture );

	std::cout << "Success<" << f->getResult()->value << ">" << std::endl;
	f->callbackCount++;
}

void IntegerFutureErrback( const Common::AbstractFuture::Ptr &aFuture )
{
	IntegerFuture::Ptr f = boost::dynamic_pointer_cast< IntegerFuture >( aFuture );

	std::cout << "Failure<" << f->getError()->value << ">" << std::endl;
	f->callbackCount--;
}

BOOST_AUTO_TEST_CASE( FutureSuccess_Test )
{
	IntegerFuture::Ptr fs( new IntegerFuture() );

	fs->addSuccessObserver( boost::bind( IntegerFutureCallback, _1 ) );
	fs->addSuccessObserver( boost::bind( IntegerFutureCallback, _1 ) );
	fs->addFailureObserver( boost::bind( IntegerFutureErrback, _1 ) );

	fs->success( Integer::Ptr( new Integer( 1 ) ) );

	BOOST_CHECK( fs->callbackCount == 2 );
}

BOOST_AUTO_TEST_CASE( FutureFailure_Test )
{
	IntegerFuture::Ptr ff( new IntegerFuture() );

	ff->addSuccessObserver( boost::bind( IntegerFutureCallback, _1 ) );
	ff->addFailureObserver( boost::bind( IntegerFutureErrback, _1 ) );
	ff->addFailureObserver( boost::bind( IntegerFutureErrback, _1 ) );

	ff->failure( Integer::Ptr( new Integer( -1 ) ) );

	BOOST_CHECK( ff->callbackCount == -2 );
}

class MyFutureList : public Common::FutureList
{
	public:
		typedef boost::shared_ptr< MyFutureList > Ptr;

	public:
		MyFutureList() : callbackCount(0) {}

		int callbackCount;
};

void MyFutureListCallback( const Common::AbstractFuture::Ptr &aFuture )
{
	MyFutureList::Ptr f = boost::dynamic_pointer_cast< MyFutureList >( aFuture );

	std::cout << "ListSuccess" << std::endl;
	f->callbackCount++;
}

void MyFutureListErrback( const Common::AbstractFuture::Ptr &aFuture )
{
	MyFutureList::Ptr f = boost::dynamic_pointer_cast< MyFutureList >( aFuture );

	std::cout << "ListFailure" << std::endl;
	f->callbackCount--;
}

BOOST_AUTO_TEST_CASE( FutureListSuccess_Test )
{
	MyFutureList::Ptr fl( new MyFutureList() );

	fl->addSuccessObserver( boost::bind( MyFutureListCallback, _1 ) );
	fl->addFailureObserver( boost::bind( MyFutureListErrback, _1 ) );

	IntegerFuture::Ptr f1( new IntegerFuture() );
	{
		f1->addSuccessObserver( boost::bind( IntegerFutureCallback, _1 ) );
		f1->addFailureObserver( boost::bind( IntegerFutureErrback, _1 ) );

		fl->addFuture( f1 );
	}

	IntegerFuture::Ptr f2( new IntegerFuture() );
	{
		f2->addSuccessObserver( boost::bind( IntegerFutureCallback, _1 ) );
		f2->addFailureObserver( boost::bind( IntegerFutureErrback, _1 ) );

		fl->addFuture( f2 );
	}

	f1->success( Integer::Ptr( new Integer( 1 ) ) );
	f2->success( Integer::Ptr( new Integer( 2 ) ) );

	BOOST_CHECK( f1->callbackCount == 1 );
	BOOST_CHECK( f2->callbackCount == 1 );
	BOOST_CHECK( fl->callbackCount == 1 );
}

BOOST_AUTO_TEST_CASE( FutureListFailure_Test )
{
	MyFutureList::Ptr fl( new MyFutureList() );

	fl->addSuccessObserver( boost::bind( MyFutureListCallback, _1 ) );
	fl->addFailureObserver( boost::bind( MyFutureListErrback, _1 ) );

	IntegerFuture::Ptr f1( new IntegerFuture() );
	{
		f1->addSuccessObserver( boost::bind( IntegerFutureCallback, _1 ) );
		f1->addFailureObserver( boost::bind( IntegerFutureErrback, _1 ) );

		fl->addFuture( f1 );
	}

	IntegerFuture::Ptr f2( new IntegerFuture() );
	{
		f2->addSuccessObserver( boost::bind( IntegerFutureCallback, _1 ) );
		f2->addFailureObserver( boost::bind( IntegerFutureErrback, _1 ) );

		fl->addFuture( f2 );
	}

	f1->success( Integer::Ptr( new Integer( 1 ) ) );
	f2->failure( Integer::Ptr( new Integer( -1 ) ) );

	BOOST_CHECK( f1->callbackCount == 1 );
	BOOST_CHECK( f2->callbackCount == -1 );
	BOOST_CHECK( fl->callbackCount == -1 );
}
